package com.jketing.service;

import java.io.Serializable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers;
import org.springframework.data.domain.ExampleMatcher.StringMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;

import com.jketing.Config;
import com.jketing.bean.PageBean;
import com.jketing.exception.SyntacticErrorException;
import com.jketing.repository.BaseRepository;


/**
 *	BaseService	服务基类
 * 
 *	功能：提供更面向表现表层的参数封装服务基类
 * 
 *	@author  李荣华
 *	@version v1.0
 *	@date    2017-11-2
 *	@since   Spring-data 1.11.4
 */
public abstract class BaseService<T, ID extends Serializable> {
	/**
	 * PredicateBuilder<T> pb 查询条件构建器，必顺由子类注入
	 */
	private PredicateBuilder<T> predicateBuilder;
	/**
	 * repository 由服务子类通过调用setRepository方法注入 .
	 */
	private BaseRepository<T, ID> baseRepository;

	public BaseRepository<T, ID> getBaseRepository() {
		return baseRepository;
	}
	public void setBaseRepository(BaseRepository<T, ID> baseRepository) {
		this.baseRepository = baseRepository;
	}
	
	public PredicateBuilder<T> getPredicateBuilder() {
		return predicateBuilder;
	}
	public void setPredicateBuilder(PredicateBuilder<T> predicateBuilder) {
		this.predicateBuilder = predicateBuilder;
	}
	
	/**
	 * Returns the number of entities available.
	 * 
	 * @return the number of entities
	 */
	public long count() {
		return baseRepository.count();
	}
	
	/**
	 * Returns the number of instances matching the given {@link Example}.
	 *
	 * @param example the {@link Example} to count instances for, can be {@literal null}.
	 * @return the number of instances matching the {@link Example}.
	 */
	<S extends T> long count(Example<S> example) {
		return baseRepository.count(example);
	}
	
	/**
	 * Returns the number of instances that the given {@link Specification} will return.
	 * 
	 * @param spec the {@link Specification} to count instances for
	 * @return the number of instances
	 */
	long count(Specification<T> spec) {
		return baseRepository.count(spec);
	}
	
	/**
	 * Returns whether an entity with the given id exists.
	 * 
	 * @param id must not be {@literal null}.
	 * @return true if an entity with the given id exists, {@literal false} otherwise
	 * @throws IllegalArgumentException if {@code id} is {@literal null}
	 */
	boolean exists(ID id) {
		return baseRepository.exists(id);
	}
	
	/**
	 * Checks whether the data store contains elements that match the given {@link Example}.
	 *
	 * @param example the {@link Example} to use for the existence check, can be {@literal null}.
	 * @return {@literal true} if the data store contains elements that match the given {@link Example}.
	 */
	<S extends T> boolean exists(Example<S> example) {
		return baseRepository.exists(example);
	}
	
	public T getOne(ID id) {
		return baseRepository.getOne(id);
	}
	
	public T findOne(ID id) {
		return baseRepository.findOne(id);
	}
	
	public <S extends T> S findOne(Example<S> example) {
		return baseRepository.findOne(example);
	}
	
	public T findOne(Specification<T> spec) {
		return baseRepository.findOne(spec);
	}
	
	/**
	 * Returns all instances of the type.
	 * 
	 * @return all entities
	 */
	public List<T> findAll() {
		return baseRepository.findAll();
	}
	
	/**
	 * Returns all instances of the type with the given IDs.
	 * 
	 * @param ids
	 * @return
	 */
	public List<T> findAll(Iterable<ID> ids) {
		return baseRepository.findAll(ids);
	}
	
	/**
	 * Returns all entities sorted by the given options.
	 * 
	 * @param sort
	 * @return all entities sorted by the given options
	 */
	public List<T> findAll(Sort sort){
		return baseRepository.findAll(sort);
	}
	
	/**
	 * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object.
	 * 
	 * @param pageable
	 * @return a page of entities
	 */
	public Page<T> findAll(Pageable pageable){
		return baseRepository.findAll(pageable);
	}
	
	/* (non-Javadoc)
	 * @see org.springframework.data.baseRepository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example)
	 */
	public <S extends T> List<S> findAll(Example<S> example){
		return baseRepository.findAll(example);
	}

	/* (non-Javadoc)
	 * @see org.springframework.data.baseRepository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Sort)
	 */
	public <S extends T> List<S> findAll(Example<S> example, Sort sort){
		return baseRepository.findAll(example, sort);
	}
	
	/**
	 * Returns a {@link Page} of entities matching the given {@link Example}. In case no match could be found, an empty
	 * {@link Page} is returned.
	 *
	 * @param example can be {@literal null}.
	 * @param pageable can be {@literal null}.
	 * @return a {@link Page} of entities matching the given {@link Example}.
	 */
	public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable){
		return baseRepository.findAll(example, pageable);
	}
	
	/**
	 * Returns all entities matching the given {@link Specification}.
	 * 
	 * @param spec
	 * @return
	 */
	public List<T> findAll(Specification<T> spec){
		return baseRepository.findAll(spec); 
	}

	/**
	 * Returns a {@link Page} of entities matching the given {@link Specification}.
	 * 
	 * @param spec
	 * @param pageable
	 * @return
	 */
	public Page<T> findAll(Specification<T> spec, Pageable pageable){
		return baseRepository.findAll(spec, pageable);
	}

	/**
	 * Returns all entities matching the given {@link Specification} and {@link Sort}.
	 * 
	 * @param spec
	 * @param sort
	 * @return
	 */
	public List<T> findAll(Specification<T> spec, Sort sort){
		return baseRepository.findAll(spec, sort);
	}
	
	/**
	 * Saves a given entity. Use the returned instance for further operations as the save operation might have changed the
	 * entity instance completely.
	 * 
	 * @param entity
	 * @return the saved entity
	 */
	public <S extends T> S save(S entity) {
		return baseRepository.save(entity);
	}
	
	/**
	 * Saves all given entities.
	 * 
	 * @param entities
	 * @return the saved entities
	 * @throws IllegalArgumentException in case the given entity is {@literal null}.
	 */
	<S extends T> Iterable<S> save(Iterable<S> entities){
		return baseRepository.save(entities);
	}
	
	/**
	 * Saves an entity and flushes changes instantly.
	 * 
	 * @param entity
	 * @return the saved entity
	 */
	public <S extends T> S saveAndFlush(S entity){
		return baseRepository.saveAndFlush(entity);
	}

	/**
	 * Deletes the entity with the given id.
	 * 
	 * @param id must not be {@literal null}.
	 * @throws IllegalArgumentException in case the given {@code id} is {@literal null}
	 */
	public void delete(ID id) {
		baseRepository.delete(id);
	}

	/**
	 * Deletes a given entity.
	 * 
	 * @param entity
	 * @throws IllegalArgumentException in case the given entity is {@literal null}.
	 */
	public void delete(T entity) {
		baseRepository.delete(entity);
	}

	/**
	 * Deletes the given entities.
	 * 
	 * @param entities
	 * @throws IllegalArgumentException in case the given {@link Iterable} is {@literal null}.
	 */
	public void delete(Iterable<? extends T> entities) {
		baseRepository.delete(entities);
	}

	/**
	 * Deletes all entities managed by the baseRepository.
	 */
	public void deleteAll() {
		baseRepository.deleteAll();
	}
	
	/**
	 * Deletes the given entities in a batch which means it will create a single {@link Query}. Assume that we will clear
	 * the {@link javax.persistence.EntityManager} after the call.
	 * 
	 * @param entities
	 */
	public void deleteInBatch(Iterable<T> entities) {
		baseRepository.deleteInBatch(entities);
	}

	/**
	 * Deletes all entities in a batch call.
	 */
	public void deleteAllInBatch() {
		baseRepository.deleteAllInBatch();
	}
	
	/**
	 * Flushes all pending changes to the database.
	 */
	public void flush() {
		baseRepository.flush();
	}

	/**
	 *  getTimeId 	得到用时间串参与生成的id
	 *  
	 *  功能:当前时间串加查询本实例有所有记录数加1做为新id
	 *  
	 *  @return String 
	 */
	protected String getTimeId(){
		StringBuilder sb = new StringBuilder();
		sb.append("PK");
		long count = baseRepository.count();
		if(count>0) {
			SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");
			sb.append(df.format(new Date()));
			count++;
			if(count < 10) {
				sb.append(String.format("%02d", count));
			}else{
				sb.append(String.valueOf(count));
			}
		}else {
			sb.append(String.format("%019d", 1));//第一条id固定不加入时间串
		}
        return sb.toString();  
	}
	
	/**
	 *  getPageRequest 	得到分页请求对象
	 *  
	 *  功能:根据排序字段名、当前页及分页大小构建分页请求对象
	 * 
	 *  @param  String 	order		表单传来的排序字段名
	 *  @param  int 	page		当前页码
	 *  @param  int 	pageSize	分页大小
	 *  
	 *  @return PageRequest 返回分页请求对象
	 */
	protected PageRequest getPageRequest(final String order, final int page, final int pageSize) {
		PageRequest request = null;
		Sort sort = null;
		String sortType = null;
		if (order != null && order.length() > 0) {
			if (order.contains("desc")) {
				int index = order.indexOf("desc");// 查找“desc”在order中的索引
				sortType = order.substring(0, index).trim();// 截取掉“desc”
				sort = new Sort(Direction.DESC, sortType);
			} else {
				sortType = order.trim();
				sort = new Sort(Direction.ASC, sortType);
			}
			request = new PageRequest(page - 1, pageSize, sort);
		} else {
			request = new PageRequest(page - 1, pageSize);
		}
		return request;
	}
	
	/**
	 *  getSort 	得到排序对象
	 *  
	 *  功能:根据排序字段名构建排序对象
	 * 
	 *  @param  String 	order		表单传来的排序字段名
	 *  
	 *  @return Sort 返回排序对象
	 */
	protected Sort getSort(final String order) {
		Sort sort = null;
		String sortType = null;
		if (order != null && order.length() > 0) {
			if (order.contains("desc")) {
				int index = order.indexOf("desc");// 查找“desc”在order中的索引
				sortType = order.substring(0, index).trim();// 截取掉“desc”
				sort = new Sort(Direction.DESC, sortType);
			} else {
				sortType = order.trim();
				sort = new Sort(Direction.ASC, sortType);
			}
		}
		return sort;
	}
	
	/**
	 *  getExampleMatcher 	得以实例查询匹配器
	 *  
	 *  功能:字符串包含、忽略大小写、id相等、忽略排序号
	 *  
	 *  @return ExampleMatcher  jpa Example 匹配器
	 */
	protected ExampleMatcher getExampleMatcher(){
		//创建匹配器，即如何使用实例查询条件
        ExampleMatcher matcher = ExampleMatcher.matching() //构建对象
                .withStringMatcher(StringMatcher.CONTAINING) //改变默认字符串匹配方式：模糊查询
                .withIgnoreCase(true) //改变默认大小写忽略方式：忽略大小写
                .withMatcher("id", GenericPropertyMatchers.exact()) //指定特定属性：实例id采用“相等”匹配的精确查询
                .withIgnorePaths("orderNumber");  //忽略属性：排序号是基本类型，做查询条件无意义
        return matcher;
	}

	/**
	 *  getSpecification 得到包含条件的规格对象
	 * 
	 *  @param  String expression 查询逻辑组合表达式
	 *  
	 *  @return Specification<T> 返回条件的规格对象
	 */
	protected Specification<T> getSpecification(final String expression){
		Specification<T> specification = null;
		
		if (StringUtils.isNotBlank(expression)) {
			specification = new Specification<T>(){
				@Override
				public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
						CriteriaBuilder cb) {
					Predicate p = null;
					try {
						p = predicateBuilder.getPredicateByExpression(expression,root,cb);
					} catch (ClassNotFoundException | ParseException | SyntacticErrorException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
	                return p;
				}
			};
		}
		return specification;
	}
	
	/**
	 *  getRootId 	得到树型结构模块根id
	 *  
	 *  所有树型结构模块规定根节点id为1
	 *  Int型id就是1
	 *  String型id格式化为17个零加1
	 *  
	 *  @return	id
	 */
	public String getRootId() {	
		StringBuilder sb = new StringBuilder();
		sb.append("PK");
		sb.append(String.format("%019d", 1));
		return sb.toString();
	}
	
	/**
	 *  getList 	得到所有记录的列表带排序
	 *  
	 *  @param  	String order 排序字段名
	 *  
	 *  @return		List<T>	结果列表
	 */
	public List<T> getList(final String order) {	
		List<T> list = null;
		Sort sort = getSort(order);
		if(sort != null){
			list = baseRepository.findAll(sort);
		}else{
			list = baseRepository.findAll();
		}
		return list;
	}

	/**
	 *  getPageBean 	得到所有记录的分页排序
	 * 	 
	 *  @param  	String order 排序字段名
	 *  @param		int currentPage	当前页码
	 *  @param		int pageSize	分页大小
	 *  
	 *  @return		PageBean<T>	分页对象
	 */
	public PageBean<T> getPageBean(final String order, final int currentPage, final int pageSize) {		
		Pageable pageable = getPageRequest(order, currentPage, pageSize);
		Page<T> page = baseRepository.findAll(pageable);
		PageBean<T> pageBean = new PageBean<T>(page.getTotalElements(),currentPage,pageSize);
		pageBean.setContent(page.getContent());
		return pageBean;
	}
	
	/**
	 *  getPageBean 	按Example(实例)查询分页排序
	 * 	 
	 *  @param 	T entity	表单传入的查询条件值实例		
	 *  @param  	String order 排序字段名
	 *  @param		int currentPage	当前页码
	 *  @param		int pageSize	分页大小
	 *  
	 *  @return		PageBean<T>	分页对象
	 */
	public PageBean<T> getPageBean(T entity, final String order, final int currentPage, final int pageSize) {		
		Pageable pageable = getPageRequest(order, currentPage, pageSize);
		Example<T> example = Example.of(entity, getExampleMatcher()); 
		Page<T> page = baseRepository.findAll(example, pageable);
		PageBean<T> pageBean = new PageBean<T>(page.getTotalElements(),currentPage,pageSize);
		pageBean.setContent(page.getContent());
		return pageBean;
	}
	
	/**
	 *  getPageBean 	解析where条件表达式查询分页排序
	 * 	 
	 *  @param 	String expression	字符串表达式		
	 *  @param  	String order 排序字段名
	 *  @param		int currentPage	当前页码
	 *  @param		int pageSize	分页大小
	 *  
	 *  @return		PageBean<T>	分页对象
	 */
	public PageBean<T> getPageBean(String expression, final String order, final int currentPage, final int pageSize) {		
		Pageable pageable = getPageRequest(order, currentPage, pageSize);
		Page<T> page = baseRepository.findAll(getSpecification(expression), pageable);
		PageBean<T> pageBean = new PageBean<T>(page.getTotalElements(),currentPage,pageSize);
		pageBean.setContent(page.getContent());
		return pageBean;
	}
	
	/**
	 *  getPageBean 	按规格构建条件查询分页排序
	 * 	 
	 *  @param 	Specification<T> specification	查询规格	
	 *  @param  String order 排序字段名
	 *  @param	int currentPage	当前页码
	 *  @param	int pageSize	分页大小
	 *  
	 *  @return		PageBean<T>	分页对象
	 */
	public PageBean<T> getPageBean(Specification<T> specification, final String order, final int currentPage, final int pageSize) {		
		Pageable pageable = getPageRequest(order, currentPage, pageSize);
		Page<T> page = baseRepository.findAll(specification, pageable);
		PageBean<T> pageBean = new PageBean<T>(page.getTotalElements(),currentPage,pageSize);
		pageBean.setContent(page.getContent());
		return pageBean;
	}
	
	/**
	 *  getList 	按Example(实例)查询不分页排序列表
	 * 	 
	 *  @param 	T entity	表单传入的查询条件值实例		
	 *  @param  	String order 排序字段名
	 *  
	 *  @return		List<T>	结果列表
	 */
	public List<T> getList(T entity, final String order) {	
		List<T> list = null;
		Sort sort = getSort(order);
		Example<T> example = Example.of(entity, getExampleMatcher()); 
		if(sort != null){
			list = baseRepository.findAll(example,sort);
		}else{
			list = baseRepository.findAll(example);
		}
		return list;
	}
	
	/**
	 *  getList 	解析where条件表达式查询不分页排序列表
	 * 	 
	 *  @param 	String expression	字符串表达式		
	 *  @param  	String order 排序字段名
	 *  
	 *  @return		List<T>	结果列表
	 */
	public List<T> getList(String expression, final String order) {	
		List<T> list = null;
		Sort sort = getSort(order);
		if(sort != null){
			list = baseRepository.findAll(getSpecification(expression),sort);
		}else{
			list = baseRepository.findAll(getSpecification(expression));
		}
		return list;
	}
	
	/**
	 *  encrypt 字符串盐值加密
	 * 	 
	 *  @param 	String code	要加密的字符串		
	 *  @param  String salt 盐值
	 *  
	 *  @return	String 加密后的字符串
	 */
	public String encrypt(String code,String salt) {
		String hashAlgorithmName = Config.getPropertyByKey("shiro.hashAlgorithmName");
		Object credentials = code;
		Object credentialsSalt = ByteSource.Util.bytes(salt);
		int hashIterations = Integer.parseInt(Config.getPropertyByKey("shiro.hashIterations"));		
		Object result = new SimpleHash(hashAlgorithmName, credentials, credentialsSalt, hashIterations);
		return result.toString();
	}
}
